package models;

import java.util.*;

import android.text.format.Time;
import android.util.Log;

/**
 * 
 * Represents a list of planned Tasks that do not conflict with each other.
 * 
 * @author Ajay Menon, Richard Pan
 *
 */

public class PlanningList {
    private Map<Long, Task> taskCollection;

    /**
     * Constructor that initializes a new empty PlanningList.
     *
    */
    public PlanningList() {
        this.taskCollection = new HashMap<Long, Task>();
    }
	
	/**
	 * Constructor that takes a collection of tasks to recreate a planning list.
	 * 
	 * I don't know if we'll need this method later, but it's useful for testing.
	*/
	public PlanningList(Map<Long, Task> taskCollection) {
		if (taskCollection == null) {
			throw new IllegalArgumentException();
		}
		this.taskCollection = taskCollection;
	}

    /**
     * 
     * @param id
     * @return Task with the given id, if it exists.
     *         Null otherwise.
     */
    public Task getTask(long id) {
        return this.taskCollection.get(id);
    }
    
    /**
     * Gets all tasks from PlanningList
     * @return List<Task> tasks that the PlanningList contains, emptyList if no tasks found
     */
    public List<Task> getAllTasks(){
    	Collection<Task> collection = taskCollection.values();
    	List<Task> tasks = new ArrayList<Task>();
    	for(Task t : collection) {
    		tasks.add(t);
    	}
    	return tasks;
    }

    /**
     * Adds a task to the Planning List. No effects if task is null.
     *
     * @param task Task to add to the planning list.
     * @requires task != null
     */
    public void addTask(Task task) {
        if (task != null) {
            this.taskCollection.put(task.getID(), task);
        }
    }

    /**
     * Removes a specific task from the Planning List if it exists.
     * No effects if the id isn't present.
     *
     * @param id The taskID to remove.
     *
     */
    public void removeTask(long id) {
        this.taskCollection.remove(id);
    }

    /**
     * Clears all tasks from the planning list.
     */
    public void clearAllTasks() {
        this.taskCollection.clear();
    }

    /**
     * Checks inside the task map to see if the added task will conflict
     * @return True if there are conflicts existing among the tasks; false if not.
     */
    public boolean hasConflict(Task task) {
    	// Task details
    	if (task.getStartTime() == null) {
    		return false;
    	}
    	
    	long id = task.getID();
    	
    	int taskStartMinute = convertToMinutes(task.getStartTime()) - task.getEstimatedTravel();
    	int taskEndMinute = getEndingMinute(task.getStartTime(), task.getEstimatedDuration());
    	boolean taskPastMidnight = (taskEndMinute < taskStartMinute);
    	
    	Log.d("New Task startminute", String.valueOf(taskStartMinute));
    	Log.d("New Task endMinute before changes", String.valueOf(taskEndMinute));
    	
    	// currently hack-y code but I don't want to change it until I'm 100% sure that it works.
    	if (taskPastMidnight) {
    		taskEndMinute += 1439; // 1439 is the # of minutes in 23 hours 59 min
    	}
    	
    	Log.d("New Task endMinute after changes", String.valueOf(taskEndMinute));

    	for (long taskID: this.taskCollection.keySet()) {
    		Task t = this.taskCollection.get(taskID);
    		if (!t.isCompleted() && taskID != id && t.getStartTime() != null) {
    			int otherStartMinute = convertToMinutes(t.getStartTime()) - t.getEstimatedTravel();
    			int otherEndMinute = getEndingMinute(t.getStartTime(), t.getEstimatedDuration());
    			
    			Log.d("Existing Task startMinute", String.valueOf(otherStartMinute));
    	    	Log.d("Existing Task endMinute before changes", String.valueOf(otherEndMinute));
    			
    			boolean otherPastMidnight = (otherEndMinute < otherStartMinute);
    			
    			if (otherPastMidnight) {
    				otherEndMinute += 1439;
    			}
    			

    			// task starts during another task.
    			if (taskStartMinute >= otherStartMinute && taskStartMinute < otherEndMinute) {
    				return true;
    			}
    			
    			// task ends during another task
    			if (taskEndMinute > otherStartMinute && taskEndMinute <= otherEndMinute) {
    				return true;
    			}
    			
    			// other tasks are scheduled during the task duration
    			if (taskStartMinute <= otherStartMinute && taskEndMinute >= otherEndMinute) {
    				return true;
    			}
    			
    			// task that is being added spans midnight
    			if (taskPastMidnight) {
    				// existing task starts during this timespan.
    				if ((otherStartMinute + 1439) >= taskStartMinute && (otherStartMinute + 1439) < taskEndMinute) {
    					return true;
    				}
    			}
    			
    			// existing task spans midnight
    			if (otherPastMidnight) {
    				// task being added starts during this timespan.
    				if ((taskStartMinute + 1439) >= otherStartMinute && (taskStartMinute + 1439) < otherEndMinute) {
    					return true;
    				}
    			}
    		}
    	}
    	
    	return false;
    }
    
    /**
     * Converts a Android.Text.Format.Time object into a minute representation used for checking conflicts
     */
    private int convertToMinutes(Time t) {
    	return (t.hour * 60) + t.minute;
    }
    
    /**
     * Given a start time and a duration, returns the ending time in minutes.
     * Used in conflict checking for when tasks start before midnight and end after midnight.
     */
    private int getEndingMinute(Time startTime, int duration) {
    	int endMinute = convertToMinutes(startTime) + duration;
    	if (endMinute > 1439) { // minutes in a 24 hour day
    		endMinute -= 1439;
    	}
    	return endMinute;
    }
}
